"""
""" unnecessary buildin plugins
"""
  let loaded_netrwSettings = 1
  let loaded_netrwFileHandlers = 1
  let loaded_2html_plugin = 1
  let loaded_getscript = 1
  let loaded_getscriptPlugin = 1
  let loaded_gzip = 1
  let loaded_logipat = 1
  " let loaded_matchit = 1
  " let loaded_syntax = 1
  let loaded_tar = 1
  let loaded_tarPlugin = 1
  let loaded_rrhelper = 1
  let loaded_spellfile_plugin = 1
  let loaded_vimball = 1
  let loaded_vimballPlugin = 1
  let loaded_zip = 1
  let loaded_zipPlugin = 1
  let loaded_tutor = 1
  let loaded_rplugin = 1
  let loaded_synmenu = 1
  let loaded_optwin = 1
  let loaded_compiler = 1
  let loaded_bugreport = 1
  let loaded_ftplugin = 1

  let did_install_default_menus = 1
  let did_install_syntax_menu = 1
  let editorconfig = 1
  let loaded_matchit = 1
  let loaded_matchparen = 1
  let loaded_sql_completion = 1
  let loaded_syntax_completion = 1
  let netrw_liststyle = 3

"""
""" common setting
"""
  let mapleader = ","
  let maplocalleader = "q"

  let g:sd_vim_dir = fnamemodify(expand('<sfile>'), ':p:h')
  let g:sd_vim_data_dir = stdpath("data")
  if has('win32') || has('win64')
    let g:home = g:sd_vim_dir
  else
    let g:home = substitute($MYVIMRC, 'init.vim', '','')
  endif

  let g:hostname = hostname()

  let g:loaded_ruby_provider = 0
  let g:loaded_perl_provider = 0
  let g:loaded_node_provider = 0
  let g:no_python_maps = 0
  " let g:loaded_python_provider = 0
  " let g:loaded_python3_provider = 0

  let &dictionary=g:home.'words'

  set title
  set noshowcmd
  set incsearch
  set noshowmode
  set nobackup
  set backupdir=/tmp/
  set noswapfile
  set showtabline=1
  " set scrolloff=0
  set omnifunc=syntaxcomplete#Complete
  set hlsearch
  set history=1000
  set listchars=tab:>\ ,trail:-,nbsp:+,eol:↩ " :list时一些特殊字符的显示↩
  set foldmethod=expr
  set nrformats=bin,hex,alpha,octal
  set diffopt+=internal,algorithm:patience
  set undodir=~/.local/share/nvim/undo_nvim/,~/.undo/

  set fileencodings=utf-8,gbk,chinese,cp936,gb18030,utf-16le,utf-16,big5,euc-jp,euc-kr,latin-1
  set fileencoding=utf-8

  set noerrorbells
  set maxmempattern=20480
  set report=99999
  set autoindent
  set cmdheight=1
  set autoread
  set nowrap
  set hidden

  " set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50,a:blinkwait500-blinkoff300-blinkon200-Cursor/lCursor,sm:block-blinkwait175-blinkoff150-blinkon175

  set redrawtime=3000

"""
""" personal setup
"""
  " disable entering Ex mode
  nnoremap Q <Nop>
  vnoremap Q <Nop>
  nnoremap q <Nop>
  vnoremap q <Nop>
  nnoremap q: <nop>
  vnoremap q: <nop>

  nnoremap  <silent> FF <esc>:w<CR>
  inoremap  <silent> FF <esc>:w<CR>
  vnoremap  <silent> FF <esc>:w<CR>

  nnoremap <silent> <Leader>m :set hlsearch!<CR>

  "go to start/end of line
  vnoremap <Leader>h ^
  vnoremap <Leader>l $
  nnoremap <Leader>h ^
  nnoremap <Leader>l $

  " scroll down/up
  vnoremap <Leader>k <C-b>M
  vnoremap <Leader>j <C-f>M
  nnoremap <Leader>k <C-b>M
  nnoremap <Leader>j <C-f>M

  " noraml resplace
  nnoremap <Leader>rr mm:%s~\v(<C-r><C-w>)~~g<Left><Left>
  vnoremap <Leader>rr "lymm:%s~\V<C-r>l~~g<Left><Left>
  " noraml resplace
  nnoremap <Leader>rw mm:%s~\v(<C-r><C-w>)~~g<Left><Left><c-r><c-w>
  vnoremap <Leader>rw "lymm:%s~\V<C-r>l~~g<Left><Left><c-r><c-/>
  " word boundary
  nnoremap <leader><Leader>r <esc>mm:echo 'search: word boundary!'<cr>:%s~\v<<c-r><c-w>>~~g<Left><Left>
  vnoremap <leader><Leader>r "ly<esc>mm:echo 'search: word boundary!'<cr>:%s~\v<<c-r>l>~~g<Left><Left>
  " current line only
  nnoremap <Leader>R mm:s~\v(<C-r><C-w>)~~g<Left><Left>
  vnoremap <Leader>R "lymm:s~\V<C-r>l~~g<Left><Left>
  " absolute no regex
  nnoremap <leader><Leader>R <esc>mm:echo 'search: absolute no regex!'<cr>:%s~\V<<c-r><c-w>>~~g<Left><Left>
  vnoremap <leader><Leader>R "ly<esc>mm:echo 'search: absolute no regex!'<cr>:%s~\V<<c-r>l>~~g<Left><Left>
  nnoremap <leader>/ :%s~\V<c-r>/~~g<Left><Left>
  vnoremap <leader>/ "ly:%s~\V<c-r>l~~g<Left><Left>

  tnoremap <silent> <Esc> <C-\><C-n>
  nnoremap <silent> <Esc> :lua Snacks.notifier.hide()<CR><esc>

  " jump to line end
  inoremap <c-e> <end>
  nnoremap <c-e> <end>
  vnoremap <c-e> <end>

  " back and forward of edit location
  nnoremap `[ g;
  nnoremap `] g,

  " paste mode
  set pastetoggle=<c-F1>

  " go through changelist
  nnoremap <silent> <M-x> g;
  nnoremap <silent> <c-]> g,

  " resize windowns
  vnoremap <silent> <space>=  :vertical resize +20<cr>
  vnoremap <silent> <space>-  :vertical resize -20<cr>
  nnoremap <silent> <space>=  :vertical resize +20<cr>
  nnoremap <silent> <space>-  :vertical resize -20<cr>

  " clean tailing whitespace while save last search keyword
  nnoremap <silent> <leader>ct mp:set ff=unix<BAR>:let _s=@/<Bar>:%s/\s\+$//e<BAR>:let @/=_s<CR>`p<CR>
  vnoremap <silent> <leader>ct :s/\s\+$//e<CR>

  " compress empty lines
  nnoremap <silent> <leader>ce mp:%s/^\_s\+\n/\r/e<CR>`p
  vnoremap <silent> <leader>ce mp:s/^\_s\+\n/\r/e<CR>`p

  " compress duplicate lines
  function Sd_compress_dup_lines() abort
    normal! mp
    exe ':%s/^\s\+$//e'
    exe ':%s/\C^\(.*\)\(\n\1\)\+$/\1/ei'
    normal! `p
  endfunction
  nnoremap <silent> <leader>cl mp:%s/\C^\(.*\)\(\n\1\)\+$/\1/ei<CR>`p
  vnoremap <silent> <leader>cl mp:s/\C^\(.*\)\(\n\1\)\+$/\1/ie<CR>`p
  " highlight duplicate lines
  nnoremap <silent> <leader><leader>cl :let @/='\C^\(.*\)\(\n\1\)\+$'<CR>n
  nnoremap <silent> <leader><leader>cL :let @/='\c^\(.*\)\(\n\1\)\+$'<CR>n

  " clean whitespace only line
  nnoremap  <silent> <leader>c<space> :g/^\s*$/d<CR>
  vnoremap  <silent> <leader>c<space> :g/^\s*$/d<CR>

  " force tab/shift width = 4
  nnoremap <silent> <leader>s4 :<C-U>setlocal expandtab tabstop=4 softtabstop=4 expandtab shiftwidth=4<BAR>:retab<CR>
  nnoremap <silent> <leader>s2 :<C-U>setlocal expandtab tabstop=2 softtabstop=2 expandtab shiftwidth=2<BAR>:retab<CR>
  vnoremap <silent> <leader>s4 <esc>:<C-U>setlocal expandtab tabstop=4 softtabstop=4 expandtab shiftwidth=4<BAR>:retab<CR>
  vnoremap <silent> <leader>s2 <esc>:<C-U>setlocal expandtab tabstop=2 softtabstop=2 expandtab shiftwidth=2<BAR>:retab<CR>

  " filter shortcut
  nnoremap  !R :r !

  " close current window
  nnoremap <silent> <localleader>w :close<CR>

  " nnoremap <C-L> <Cmd>nohlsearch<Bar>diffupdate<Bar>normal! <C-L><CR>
  inoremap <C-U> <C-G>u<C-U>
  inoremap <C-W> <C-G>u<C-W>
  nnoremap & :&&<CR>
  inoremap <C-K> <C-o>D

  " shortcut for command mode
  nnoremap <silent> <M-v> o<ESC>"+p
  vnoremap <silent> <M-v> "+p
  inoremap <silent> <M-v> <c-r>+
  cnoremap <silent> <M-v> <c-r>+
  " <c-q> is mapped to <c-a> by autohotkey
  cnoremap <expr> <c-a> '<home>'
  cnoremap <expr> <c-e> '<end>'
  cnoremap <expr> <c-d> '<del>'
  cnoremap <expr> <c-k> '<del>'
  inoremap  <c-d> <Del>

  " quick format
  nnoremap <silent> <leader>= mpgg=G`pzz
  " nnoremap <silent> <leader>= V=<ESC>

  " left/right quick shortcut
  nnoremap ch c^
  nnoremap cl cg_
  nnoremap dh d^
  nnoremap dl dg_
  nnoremap yh y^
  nnoremap yl yg_
  " nnoremap Y y$
  nnoremap dc g0D
  nnoremap vh v^
  nnoremap vl vg_
  nnoremap vv ^vg_

  " quick surround word
  nnoremap <silent> <leader>> viw<esc>a><esc>bi<<esc>l
  nnoremap <silent> <leader>" viw<esc>a"<esc>bi"<esc>l
  nnoremap <silent> <leader>' viw<esc>a'<esc>bi'<esc>l
  nnoremap <silent> <leader>( viw<esc>a)<esc>bi(<esc>l
  nnoremap <silent> <leader>) viw<esc>a)<esc>bi(<esc>l
  nnoremap <silent> <leader>B viw<esc>a}<esc>bi{<esc>l
  nnoremap <silent> <leader>B viw<esc>a}<esc>bi{<esc>l
  nnoremap <silent> <leader>[ viw<esc>a]<esc>bi[<esc>l
  nnoremap <silent> <leader>] viw<esc>a]<esc>bi[<esc>l
  " quick surround visual selection
  vnoremap <silent> <leader>> <esc>`>a><esc>`<i<<esc>
  vnoremap <silent> <leader>" <esc>`>a"<esc>`<i"<esc>
  vnoremap <silent> <leader>' <esc>`>a'<esc>`<i'<esc>
  vnoremap <silent> <leader>( <esc>`>a)<esc>`<i(<esc>
  vnoremap <silent> <leader>) <esc>`>a)<esc>`<i(<esc>
  vnoremap <silent> <leader>B <esc>`>a}<esc>`<i{<esc>
  vnoremap <silent> <leader>B <esc>`>a}<esc>`<i{<esc>
  vnoremap <silent> <leader>[ <esc>`>a]<esc>`<i[<esc>
  vnoremap <silent> <leader>] <esc>`>a]<esc>`<i[<esc>

  " toggle modifiable
  nnoremap <silent> <f5> :if &modifiable \| setl nomodifiable \| echo 'Current buffer is now non-modifiable' \| else \| setl modifiable \| echo 'Current buffer is now modifiable' \| endif<CR>
  vnoremap <silent> <f5> <ESC>:if &modifiable \| setl nomodifiable \| echo 'Current buffer is now non-modifiable' \| else \| setl modifiable \| echo 'Current buffer is now modifiable' \| endif<CR>

  " :ascii ga  g8
  nnoremap  <silent> g7 ga

  " yank and paste
  nnoremap  yp yyp

  " select the pasted block
  nnoremap <expr> gp '`[' . strpart(getregtype(), 0, 1) . '`]'

  " macro
  nnoremap <localleader>e qe
  nnoremap @E @e

  nnoremap <silent> <leader>MD :call mkdir(expand("%:p:h"), "p")<CR>
  " nnoremap <silent> <leader>MD :!mkdir -p %:p:h<CR>

  nnoremap z/ :set hlsearch<CR>/\V
  nnoremap m/ :set hlsearch<CR>/\v^

  " nnoremap <silent> <leader>sc :set termguicolors!<CR>

  " nnoremap <silent> Q! <esc>:q!<CR>
  " vnoremap <silent> Q! <esc><esc>:q!<CR>
  nnoremap <silent> ZQ <esc>:quitall!<CR>
  vnoremap <silent> ZQ <esc><esc>:quitall!<CR>
  tnoremap <silent> ZQ <esc><esc>:quitall!<CR>

  nnoremap <silent> <leader>cs :s/\\/\//g<CR>
  vnoremap <silent> <leader>cs :s/\\/\//g<CR>

  map gm gM

"""
""" folding setting
"""
  set foldopen-=search
  set foldlevel=99
  nnoremap <silent> z1 :setlocal foldlevel=0<CR>
  nnoremap <silent> z2 :setlocal foldlevel=1<CR>
  nnoremap <silent> z3 :setlocal foldlevel=2<CR>
  nnoremap <silent> z4 :setlocal foldlevel=3<CR>
  nnoremap <silent> z5 :setlocal foldlevel=4<CR>
  nnoremap <silent> z6 :setlocal foldlevel=99<CR>

  function! Sd_fold_text()
    if &ft == 'norg'
      let begin_space  = repeat(' ' , GetPotionFold(v:foldstart) * &shiftwidth)
      let lines = v:foldend - v:foldstart + 1

      let dots_num = 88 - len(begin_space) - 5
      if dots_num % 2 " odd
        let dots =  repeat(' .',(dots_num + 1)/2)
      else " even
        let dots = ' ' . repeat(' .', dots_num/2)
      endif
      return begin_space .  dots . repeat(' ', 4 - len(lines)). lines
    else
      let first_line = substitute(getline(v:foldstart), "\t", "  ", "g")
      let step = 5
      let count = 0
      while strlen(first_line)  > 73
        let first_line = strcharpart(first_line, 0, 75 - count * step)
        let count += 1
      endwhile

      let lines = v:foldend - v:foldstart + 1

      let count = 0
      for item in split(first_line, '\zs')
        if char2nr(item) > 127
          let count += 1
        endif
      endfor

      let dots_num = 88 - len(first_line) - 5 + count
      if dots_num % 2 " odd
        let dots =  repeat(' .',(dots_num + 1)/2)
      else " even
        let dots = ' ' . repeat(' .', dots_num/2)
      endif

      return first_line .  dots . repeat(' ', 4 - len(lines)). lines
    endif
  endfunction

  function! GetPotionFold(lnum)
    if getline(a:lnum) =~? '\v^\s*$'
      return '-1'
    endif

    if &ft == 'norg'
      if getline(a:lnum) =~? '\v^\s*\*\s.*$'
        return 0
      elseif getline(a:lnum) =~? '\v^\s*\*\*\s.*$'
        return 1
      elseif getline(a:lnum) =~? '\v^\s*\*\*\*\s.*$'
        return 2
      elseif getline(a:lnum) =~? '\v^\s*\*\*\*\*\s.*$'
        return 3
      elseif getline(a:lnum) =~? '\v^\s*\*\*\*\*\*\s.*$'
        return 4
      elseif getline(a:lnum) =~? '\v^\s*\*\*\*\*\*\*\s.*$'
        return 5
      else
      endif
    endif

    let this_indent = IndentLevel(a:lnum)
    let next_indent = IndentLevel(NextNonBlankLine(a:lnum))

    if next_indent == this_indent
      return this_indent
    elseif next_indent < this_indent
      return this_indent
    elseif next_indent > this_indent
      return '>' . next_indent
    endif
  endfunction

  function! NextNonBlankLine(lnum)
    let numlines = line('$')
    let current = a:lnum + 1

    while current <= numlines
      if getline(current) =~? '\v\S'
        return current
      endif

      let current += 1
    endwhile

    return -2
  endfunction

  function! IndentLevel(lnum)
    return indent(a:lnum) / &shiftwidth
  endfunction

  set fillchars=vert:\|,stl:\ ,stlnc:\ ,fold:\ "  在被分割的窗口间显示空白,便于阅读
  set foldminlines=1
  set foldnestmax=9
  set foldcolumn=0
  set foldignore=

  function! Sd_toggle_fold()
    if foldlevel('.') == 0
      " normal! mt
      while foldlevel('.') == 0 && line('.') != line('$')
        normal! j
      endwhile
      if foldlevel('.') == 0
        call Sd_echo('no foldlevel found below current line!')
      else
        if foldclosed('') == -1
          silent normal! za
        else
          silent normal! zO
        endif
      endif
    else
        if foldclosed('') == -1
          silent normal! za
        else
          silent normal! zO
        endif
    endif
  endfunction

  function! Sd_toggle_fold_r()
    if foldclosed('.') > 0
      normal! zO
    else
      if foldlevel('.') != 0
        normal! zczO
      else
        while foldlevel('.') == 0 && line('.') != line('$')
          normal! j
        endwhile
        if foldlevel('.') != 0
          normal! zczO
        endif
      endif
    endif
  endfunction

  nnoremap <silent> za :call Sd_toggle_fold()<cr>
  nnoremap <silent> zr :call Sd_toggle_fold_r()<cr>

"""
""" util function
"""
  function! Sd_get_begin_whitespace(ln)
    return len(substitute(getline(a:ln), '\S.*','', ''))
  endfunction

  function! Sd_echo(...)
    let l:color = [ 'WarningMsg', 'Error' ]
    if a:0 == 1
      let l:i = 1
    elseif a:0 == 2
      let l:i = a:2
    else
      return
    endif
    exe 'echohl ' . l:color[l:i]
    echo '  ' . l:color[l:i] . ': '
    echohl None
    echon a:1
  endfunction

  function! Sd_get_charactor_under_cursor()
    return matchstr(getline('.'), '\%' . col('.') . 'c.')
  endfunction

  function! Sd_reselect_last_paste_block(...)
    if a:0 == 0
      let l:v_mode = "\<C-v>"
    else
      let l:v_mode = a:1
    endif

    exe "normal! `["
    exe "normal! " . l:v_mode
    exe "normal! `]"
  endfunction

  function! Get_visual_selection()
    let [line_start, column_start] = getpos("'<")[1:2]
    let [line_end, column_end] = getpos("'>")[1:2]
    let lines = getline(line_start, line_end)
    if len(lines) == 0
      return ''
    endif
    let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
    let lines[0] = lines[0][column_start - 1:]
    return join(lines, "\n")
  endfunction

  function! CloseAnyTermWindow()
    silent! call Sd_close_any_floating_win()
    if bufname('%') !~ '^term://' && bufname('%') !~ '^\!'
      let l:curr_buf = bufnr('%')
    endif
    " silent! windo if bufname('%') =~ '^term://' || bufname('%') =~ '^\!'  | bwipeout! | endif
    silent! bufdo if bufname('%') =~ '^term://' || bufname('%') =~ '^\!'  | bwipeout! | endif
    if exists('l:curr_buf')
      silent! exe ':buffer ' . l:curr_buf
      normal! zz
    endif
  endfunction

  function! CloseAnyTermWindow02() abort
    silent! call Sd_close_any_floating_win()
    silent! exe 'AerialCloseAll'
    silent! exe 'cclose'
    silent! exe 'TroubleClose'
    let current_winnr = 0
    if bufname('%') !~ '^term://' && bufname('%') !~ '^\!'
      let current_winnr = winnr()
    endif
    silent! windo if bufname('%') =~ '^term://' || bufname('%') =~ '^\!' | if winnr() < current_winnr | let current_winnr -= 1 | endif | bwipeout! | endif
    if current_winnr != 0
      " silent! exe ':buffer ' . l:curr_buf
      silent! exe ':1wincmd w'
      while  winnr() < current_winnr && winnr() != winnr('$')
        silent! exe ':wincmd l'
      endwhile
    endif
  endfunction

  function! GetActiveWinNameList()
    silent! call Sd_close_any_floating_win()
    let blist = getbufinfo({ 'buflisted': 1})
    let result = []
    for item in blist
      "skip unnamed buffers; also skip hidden buffers?
      if empty(item.name) || item.hidden
        continue
      endif
      if item.loaded == 1
        let item_index = index(result, item.name)
        if item_index < 0 "item not in list
          call add(result, shellescape(item.name))
        endif
      endif
    endfor
    return result
  endfunction

  function! GetActiveBufferNumList()
    let l:blist = getbufinfo({ 'buflisted': 1})
    let l:result = []
    for l:item in l:blist
      "skip unnamed buffers; also skip hidden buffers?
      if empty(l:item.name) || l:item.hidden
        continue
      endif
      call add(l:result, shellescape(l:item.bufnr))
    endfor
    return l:result
  endfunction

  function! GetActiveBufferNameList()
    let l:blist = getbufinfo({'bufloaded': 1, 'buflisted': 1})
    let l:blist = getbufinfo({ 'buflisted': 1})
    let l:result = []
    for l:item in l:blist
      "skip unnamed buffers; also skip hidden buffers?
      if empty(l:item.name) || l:item.hidden
        continue
      endif
      call add(l:result, shellescape(l:item.name))
    endfor
    return l:result
  endfunction

  function! InputChar()
    let c = getchar()
    return type(c) == type(0) ? nr2char(c) : c
  endfunction

  function! IsBlank( bufnr )
    return (empty(bufname(a:bufnr)) &&
          \ getbufvar(a:bufnr, '&modified') == 0 &&
          \ empty(getbufvar(a:bufnr, '&buftype'))
          \)
  endfunction

  function! ExistOtherBuffers( targetBufNr )
    return ! empty(filter(range(1, bufnr('$')), 'buflisted(v:val) && v:val != a:targetBufNr'))
  endfunction

  function! CurrentWindowIsEmptyVim()
    let l:currentBufNr = bufnr('')
    return IsBlank(l:currentBufNr) && ! ExistOtherBuffers(l:currentBufNr)
  endfunction

"""
""" close any other windows
"""
  function! Sd_close_any_floating_win() abort
    " silent! call coc#float#close_all()
    silent! lua Snacks.notifier.hide()
    for item in nvim_list_wins()
      let config = nvim_win_get_config(item)
      if config['relative'] != ""
        call nvim_win_close(item, 1)
      endif
    endfor
  endfunction

  function! CloseAnyOtherWindow(...)
    silent! lua Snacks.notifier.hide()
    silent! call Sd_close_any_floating_win()
    " exe 'normal <c-w><c-o>'
    " normal mW
    let l:current_buf_n = bufnr('%')
    windo if l:current_buf_n != bufnr('%') && &ft != 'nerdtree' && &ft != 'netrw' | close! | endif
    if winnr('$') >= 2
      silent! exe 'only'
    endif

    if a:0 >=1 && a:1 == 'WipeAuxiliaryBuf' && &ft !~ '^sd-'
      let l:current_buf_n = bufnr("%")
      silent! bufdo if &ft =~ '^sd-' | bwipeout! | endif
      exe ":buffer " . l:current_buf_n
    endif
  endfunction

  nnoremap <silent> <leader><CR> :<c-u>silent! call CloseAnyOtherWindow('WipeAuxiliaryBuf')<cr>
  vnoremap <silent> <leader><CR> :<c-u>silent! call CloseAnyOtherWindow('WipeAuxiliaryBuf')<cr>

  nnoremap <silent> Q? :<c-u>call CloseAnyTermWindow02()<cr>
  vnoremap <silent> Q? <ESC>:<c-u>call CloseAnyTermWindow02()<cr>
  nnoremap <silent> <localleader>? :<c-u>call CloseAnyTermWindow()<cr>
  vnoremap <silent> <localleader>? <ESC>:<c-u>call CloseAnyTermWindow()<cr>

"""
""" plugin: vim-oscyank
"""
  " https://github.com/ojroques/vim-oscyank
  let g:oscyank_max_length = 800000

  if exists('g:loaded_oscyank') || &compatible
    finish
  endif

  let g:loaded_oscyank = 1

  " Send a string to the terminal's clipboard using OSC52.
  function! OSCYankString(str)
    let length = strlen(a:str)
    let limit = get(g:, 'oscyank_max_length', 100000)
    let osc52_key = 'default'

    if length > limit
      echohl WarningMsg
      echo '[oscyank] Selection has length ' . length . ', limit is ' . limit
      echohl None
      return
    endif

    if exists('g:oscyank_term')  " Explicitly use a supported terminal.
      let osc52_key = get(g:, 'oscyank_term')
    else  " Fallback to auto-detection.
      if !empty($TMUX)
        let osc52_key = 'tmux'
      elseif match($TERM, 'screen') > -1
        let osc52_key = 'screen'
      elseif match($TERM, 'kitty') > -1
        let osc52_key = 'kitty'
      endif
    endif

    let osc52 = get(s:osc52_table, osc52_key, s:osc52_table['default'])(a:str)
    call s:raw_echo(osc52)

    if !get(g:, 'oscyank_silent', 0)
      echo '[oscyank] ' . length . ' characters copied'
    endif
  endfunction

  " Send the visual selection to the terminal's clipboard using OSC52.
  " https://stackoverflow.com/questions/1533565/how-to-get-visually-selected-text-in-vimscript
  function! OSCYankVisual() range
    let [line_start, column_start] = getpos("'<")[1:2]
    let [line_end, column_end] = getpos("'>")[1:2]

    let lines = getline(line_start, line_end)
    if len(lines) == 0
      return ''
    endif

    let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
    let lines[0] = lines[0][column_start - 1:]

    call OSCYankString(join(lines, "\n"))
    execute "normal! `<"
  endfunction

  " Send the input text object to the terminal's clipboard using OSC52.
  function! OSCYankOperator(type, ...) abort
    " Special case: if the user _has_ explicitly specified a register (or
    " they've just supplied one of the possible defaults), OSCYank its contents.
    if !(v:register ==# '"' || v:register ==# '*' || v:register ==# '+')
      call OSCYankString(getreg(v:register))
      return ''
    endif

    " Otherwise, do the usual operator dance (see `:help g@`).
    if a:type == ''
      set opfunc=OSCYankOperator
      return 'g@'
    endif

    let [line_start, column_start] = getpos("'[")[1:2]
    let [line_end, column_end] = getpos("']")[1:2]

    let lines = getline(line_start, line_end)
    if len(lines) == 0
      return ''
    endif

    if a:type ==# "char"
      let lines[-1] = lines[-1][: column_end - (&selection == 'inclusive' ? 1 : 2)]
      let lines[0] = lines[0][column_start - 1:]
    endif

    call OSCYankString(join(lines, "\n"))
  endfunction

  " This function base64's the entire string and wraps it in a single OSC52.
  " It's appropriate when running in a raw terminal that supports OSC 52.
  function! s:get_OSC52(str)
    let b64 = s:b64encode(a:str, 0)
    return "\e]52;c;" . b64 . "\x07"
  endfunction

  " This function base64's the entire string and wraps it in a single OSC52 for
  " tmux.
  " This is for `tmux` sessions which filters OSC52 locally.
  function! s:get_OSC52_tmux(str)
    let b64 = s:b64encode(a:str, 0)
    return "\ePtmux;\e\e]52;c;" . b64 . "\x07\e\\"
  endfunction

  function! s:get_OSC52_DCS(str)
    let b64 = s:b64encode(a:str, 76)
    " Remove the trailing newline.
    let b64 = substitute(b64, '\n*$', '', '')
    " Replace each newline with an <end-dcs><start-dcs> pair.
    let b64 = substitute(b64, '\n', "\e/\eP", "g")
    " (except end-of-dcs is "ESC \", begin is "ESC P", and I can't figure out
    " how to express "ESC \ ESC P" in a single string. So the first substitute
    " uses "ESC / ESC P" and the second one swaps out the "/". It seems like
    " there should be a better way.)
    let b64 = substitute(b64, '/', '\', 'g')
    " Now wrap the whole thing in <start-dcs><start-osc52>...<end-osc52><end-dcs>.
    return "\eP\e]52;c;" . b64 . "\x07\e\x5c"
  endfunction

  " Kitty versions below 0.22.0 require the clipboard to be flushed before
  " accepting a new string.
  " https://sw.kovidgoyal.net/kitty/changelog/#id33
  function! s:get_OSC52_kitty(str)
    call s:raw_echo("\e]52;c;!\x07")
    return s:get_OSC52(a:str)
  endfunction

  " Echo a string to the terminal without munging the escape sequences.
  function! s:raw_echo(str)
    if has('win32') && has('nvim')
      call chansend(v:stderr, a:str)
    else
      if filewritable('/dev/fd/2')
        call writefile([a:str], '/dev/fd/2', 'b')
      else
        exec("silent! !echo " . shellescape(a:str))
        redraw!
      endif
    endif
  endfunction

  " Encode a string of bytes in base 64.
  " If size is > 0 the output will be line wrapped every `size` chars.
  function! s:b64encode(str, size)
    let bytes = s:str2bytes(a:str)
    let b64_arr = []

    for i in range(0, len(bytes) - 1, 3)
      let n = bytes[i] * 0x10000
            \ + get(bytes, i + 1, 0) * 0x100
            \ + get(bytes, i + 2, 0)
      call add(b64_arr, s:b64_table[n / 0x40000])
      call add(b64_arr, s:b64_table[n / 0x1000 % 0x40])
      call add(b64_arr, s:b64_table[n / 0x40 % 0x40])
      call add(b64_arr, s:b64_table[n % 0x40])
    endfor

    if len(bytes) % 3 == 1
      let b64_arr[-1] = '='
      let b64_arr[-2] = '='
    endif

    if len(bytes) % 3 == 2
      let b64_arr[-1] = '='
    endif

    let b64 = join(b64_arr, '')
    if a:size <= 0
      return b64
    endif

    let chunked = ''
    while strlen(b64) > 0
      let chunked .= strpart(b64, 0, a:size) . "\n"
      let b64 = strpart(b64, a:size)
    endwhile

    return chunked
  endfunction

  function! s:str2bytes(str)
    return map(range(len(a:str)), 'char2nr(a:str[v:val])')
  endfunction

  " Lookup table for g:oscyank_term.
  let s:osc52_table = {
        \ 'default': function('s:get_OSC52'),
        \ 'kitty': function('s:get_OSC52_kitty'),
        \ 'screen': function('s:get_OSC52_DCS'),
        \ 'tmux': function('s:get_OSC52_tmux'),
        \ }

  " Lookup table for s:b64encode.
  let s:b64_table = [
        \ "A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P",
        \ "Q","R","S","T","U","V","W","X","Y","Z","a","b","c","d","e","f",
        \ "g","h","i","j","k","l","m","n","o","p","q","r","s","t","u","v",
        \ "w","x","y","z","0","1","2","3","4","5","6","7","8","9","+","/",
        \ ]

  nnoremap <silent> <expr> <Plug>OSCYank OSCYankOperator('')

  command! -range OSCYank <line1>,<line2>call OSCYankVisual()
  command! -nargs=1 OSCYankReg call OSCYankString(getreg(<f-args>))

  if has('unix')
    " vnoremap <silent> <leader><leader>y :OSCYank<CR>
    "vnoremap <silent> cg :OSCYank<CR>
    "nnoremap <silent> cae mpggVG:OSCYank<CR>`pzz
    nnoremap <silent> <leader><leader>f :let @"=expand("%:p")<BAR>:silent call OSCYankString(@")<BAR>:echo "copy file-path to +"<CR>
    nnoremap <silent> <leader><leader>n :let @"=expand("%:t")<BAR>:silent call OSCYankString(@")<BAR>:echo "copy file-name to +"<CR>
    nnoremap <silent> <leader><leader>b :let @"=expand("%:p:h")<BAR>:silent call OSCYankString(@")<BAR>:echo "copy file-dir to +"<CR>
  endif

"""
""" hex and decimal convert
"""
  function! Sd_convert()
    normal  "iyiw
    if @i =~ '0x[a-f0-9]\+'
      echo 'Convert ' . @i . ' : ' str2nr(@i, 16)
      let @i = str2nr(@i, 16)
    elseif @i =~ '^\d\+$'
      echo  'Convert ' . @i . ' : 0x' . printf('%x', @i)
      let @i = '0x' . printf('%x', @i)
    else
      call Sd_echo('not a hex or decimal !')
    endif
  endfunction
  nnoremap  <silent> <F8> :call Sd_convert()<CR>

"""
""" quick_yank_word_under_cursor
"""
  function! Sd_quick_yank() abort
    while Sd_get_charactor_under_cursor() == ' '
      normal w
    endwhile
    let sd_current_char = char2nr(Sd_get_charactor_under_cursor())
    if  (sd_current_char && sd_current_char < 48 ) || (57 < sd_current_char && sd_current_char < 65) || (90 < sd_current_char && sd_current_char < 95) || (122 < sd_current_char && sd_current_char) || (sd_current_char == 0)
      normal y1l
    else
      normal yiw
    endif
    if has("win32") || has("win64")
      let @+=@"
    else
      call OSCYankString(@")
    endif
  endfunction

  nnoremap <silent> <space><space> :call Sd_quick_yank()<CR>

"""
""" operator cd ;
"""
  nnoremap <silent> ; :set opfunc=Sd_copy_to_clipboard<CR>g@
  vnoremap <silent> ; :<C-U>call Sd_copy_to_clipboard(visualmode(), 1)<CR>
  "nnoremap <silent> cg :set opfunc=Sd_copy_to_clipboard<CR>g@
  vnoremap <silent> cg :<C-U>call Sd_copy_to_clipboard(visualmode(), 1)<CR>
  nnoremap <silent> cg :silent call OSCYankString(trim(getline('.')))<BAR>: echo 'current line copied to +'<CR>

  function! Sd_copy_to_clipboard(type, ...)
    let sel_save = &selection
    " let reg_save = getreginfo('"')
    let reg_save = @@
    let cb_save = &clipboard
    let visual_marks_save = [getpos("'<"), getpos("'>")]
    try
      if a:0
        silent exe "normal! gvy"
      else
        set clipboard= selection=inclusive
        let commands = {'line': "'[V']y", 'char': "`[v`]y", 'block': "`[\<c-v>`]y"}
        silent exe 'noautocmd keepjumps normal! ' . get(commands, a:type, '')
      endif
      if has("win32") || has("win64")
        if  line("'[") == line("']")
          let @+=trim(getreg('"'))
        else
          let @+=getreg('"')
        endif
        echom 'content covered by movement copied to system clipboard!'
      else
        if  line("'[") == line("']")
          call OSCYankString(trim(getreg('"')))
        else
          call OSCYankString(getreg('"'))
        endif
      endif
    finally
      " call setreg('"', reg_save)
      let @@=reg_save
      call setpos("'<", visual_marks_save[0])
      call setpos("'>", visual_marks_save[1])
      let &clipboard = cb_save
      let &selection = sel_save
    endtry
  endfunction

  nnoremap <silent> ;l :exe "normal ;$"<CR>
  nnoremap <silent> ;h :exe "normal ;^"<CR>
  nnoremap <silent> ;; :silent call OSCYankString(trim(getline('.')))<BAR>: echo 'current line copied to +'<CR>

"""
""" env path modification
"""
  if ! (has('win32') || has('win64'))
    " let $PATH = $PATH . ':' . g:sd_vim_dir . '/mason/bin/'
    let $PATH = g:home . 'software/node/bin/' . ':' . $PATH
    let $PATH = g:home . 'software/fd/' . ':' . $PATH
    let $PATH = g:home . 'software/' . ':' . $PATH
  endif

"""
""" restore session """ session management
"""
  if exists('g:sd_vim_session_dir') == 0
    let g:sd_vim_session_dir = g:sd_vim_data_dir . '/'
  endif

  function! Sd_restore_last_session() abort
    silent! exe 'source ' . g:sd_vim_session_dir . 'lastsession.vim'
    for i in range(1, bufnr('$'))
      " Check if the buffer is listed and doesn't have a file name
      if buflisted(i) && empty(bufname(i))
        " Delete the buffer
        execute 'bdelete ' . i
      endif
    endfor
    let g:current_session = 'lastsession'
  endfunction

  function! Sd_vim_leavePre() abort
    silent! exe ':mksession! ' . g:sd_vim_session_dir . 'lastsession.vim'
  endfunction

  augroup SeadogVimExit01
    autocmd!
    autocmd VimLeavePre * nested call Sd_vim_leavePre()
  augroup end

  let g:current_session = ""

  function! Sd_complete_list_session(ArgLead, CmdLine, CursorPos, ...)
    let myList = readdir(g:sd_vim_session_dir)
    return filter(myList, 'v:val =~ "^'. a:ArgLead .'.*\\.vim$"')
  endfunction

  function! Sd_session_management(opt)
    if a:opt == 'save'
      if g:current_session == ""  && !empty(v:this_session)
        let l:user_input = input("session-name > ", substitute(v:this_session, '^.*\(\/\|\\\)','',''), "customlist,Sd_complete_list_session")
      else
        let l:user_input = input("session-name > ", g:current_session, "customlist,Sd_complete_list_session")
      endif
      if l:user_input =~ "\<esc>" || l:user_input =~ "\<c-c>" || l:user_input =~ "^\s*$"
        return 1
      endif
      let g:current_session = substitute(l:user_input, '\.vim$', '', 'g')
      exe ':mksession!' . g:sd_vim_session_dir . g:current_session . '.vim'
      return 0
    elseif a:opt == 'load'
      let l:user_input = input("session-name > ", "", "customlist,Sd_complete_list_session")
      if l:user_input =~ "\<esc>" || l:user_input =~ "\<c-c>" || l:user_input =~ "^\s*$"
        return 1
      endif
      let l:user_input = substitute(l:user_input, '\.vim$', '', 'g')
      let l:target_session = substitute( g:sd_vim_session_dir . l:user_input . '.vim', '\(\\\|\/\)', '/', 'g')
      if ! filereadable(l:target_session)
        call Sd_echo(' Not a invalid session name!')
        return 0
      endif
      let g:current_session = l:user_input
    else   "swith session using <leader>o
      let l:target_session = substitute( g:sd_vim_session_dir . a:opt, '\(\\\|\/\)', '/', 'g')
      let g:current_session = a:opt
    endif
    exe ':1,$bd!'
    exe 'source  ' . l:target_session
  endfunction

  nnoremap <silent> <leader><leader>ss :call Sd_session_management('save')<CR>
  nnoremap <silent> <leader><leader>ll :call Sd_session_management('load')<CR>

"""
""" !!! lazy plugins
"""
  lua require("config.lazy")

"""
""" highlight setting
"""
  hi! WhiteSpaceEOL   ctermbg=16          guibg=#000000
  hi! StrongNote      ctermfg=red         guifg=red
  hi! NormalNote      guifg=#000000       guibg=#00f6b8 guifg=#000000
  hi! NormalNoteTODO  guifg=#000000       guibg=#ffbc03 guifg=#000000
  hi! httpURL         cterm=bold          gui=bold      guifg=#50fa7b

  hi! clear           Todo
  hi! clear           luaTodo
  hi! clear           vimTodo
  hi! clear           confTodo

  hi! StrongMark      ctermfg=yellow      guifg=yellow  guibg=#000000 gui=bold
  hi! StrongMarkSub01 ctermfg=lightyellow guifg=#50fa7b guibg=#000000 gui=bold
  hi! StrongMarkSub02 ctermfg=darkyellow  guifg=#43decc guibg=#000000 gui=bold

"""
""" tab operation
"""
  function Sd_tab_switch() abort
    if tabpagenr('$') == 1
      silent! exe 'tab split'
    else
      silent! exe 'tab tabnext'
    endif
  endfunction
  " nnoremap <leader>to        :tabonly<cr>
  nnoremap <silent> <leader>tc :tab split<cr>
  nnoremap <silent> <leader>tt :tab split<cr>
  nnoremap <silent> <leader>tk :tabclose<cr>
  nnoremap <silent> <leader>tn :tabnext<cr>
  nnoremap <silent> <tab>      :call Sd_tab_switch()<cr>
  vnoremap <silent> <tab>      <esc>:call Sd_tab_switch()<cr>
  nnoremap <silent> <leader>tp :tabprevious<cr>
  nnoremap <silent> <s-tab>    :tabprevious<cr>
  vnoremap <silent> <s-tab>    <esc>:tabprevious<cr>
  nnoremap <silent> <leader>t1 :normal! 1gt<cr>
  nnoremap <silent> <leader>t2 :normal! 2gt<cr>
  nnoremap <silent> <leader>t3 :normal! 3gt<cr>
  nnoremap <silent> <leader>t4 :normal! 4gt<cr>
  nnoremap <silent> <leader>t5 :normal! 5gt<cr>
  nnoremap <silent> <leader>t6 :normal! 6gt<cr>
  nnoremap <silent> <leader>t7 :normal! 7gt<cr>

  nnoremap <silent> <leader>td :tab split<BAR>normal! gd<CR>
  nnoremap <silent> <leader>tw :tab split<BAR>vertical split<BAR>normal gd<CR>

"""
""" text.vim
"""
  function! Sd_text_enter() abort
    let curr_line = getline('.')
    if curr_line =~ '\v\{\/\s.+\}\[.+\]'
      let target_file = substitute(curr_line, '\v\{\/\s(.+)\}\[.+\]', '\1', 'g')
      " exe 'lcd %:p:h'
      silent! exe 'Rooter'
      silent! exe 'edit '. target_file
    else
      call feedkeys("\<CR>", "in")
    endif
  endfunction

"""
""" tmux
"""
  if exists('$TMUX')
    if filereadable('~/.config/nvim/software/tmux/tmux.conf')
      let g:sd_tmux = '~/.config/nvim/software/tmux/tmux.linux-amd64'
    else
      let g:sd_tmux = 'tmux'
    endif
    if g:hostname == 'apple'
      nnoremap <silent> <leader>tw :mksession! $VIM/lastsession.vim <BAR>call system(g:sd_tmux . ' new-window -a -c "#{pane_current_path}" "/bin/sh -c \"~/files_saved/nvim-lua/nvim-linux64/bin/nvim; exec bash\""')<CR>
    else
      nnoremap <silent> <leader>tw :mksession! $VIM/lastsession.vim <BAR>call system(g:sd_tmux . ' new-window -a -c "#{pane_current_path}" "/bin/sh -c \"~/.files_saved/neovim/bin/nvim; exec bash\""')<CR>
    endif
    augroup tmux_something
      autocmd FocusGained,VimResume,VimEnter * call system(g:sd_tmux . ' set status off')
      autocmd VimLeave,VimSuspend * call system(g:sd_tmux . ' set status on')
      " autocmd TabEnter * call Sd_tab_enter()
    augroup END
  endif

"""
""" lazygit
"""
  function! Sd_lazygit() abort
    if exists('$TMUX')
      silent! exe 'Rooter'
      call system(g:sd_tmux . ' new-window -a -c "#{pane_current_path}" lazygit')
    else
      lua LazyVim.lazygit( { cwd = LazyVim.root.git() })
    endif
  endfunction
  nnoremap <silent> <leader>gt :call Sd_lazygit()<CR>

"""
""" vim-easy-align
"""
  vnoremap A :EasyAlign /<left>

"""
""" final setting
"""
  function! Sd_set_foldlevel()
    if &ft == 'norg'
      return
    endif
    if line('$') < 90
      setlocal foldlevel=99
    elseif line('$') < 180
      setlocal foldlevel=1
    else
      setlocal foldlevel=0
    endif
  endfunction

  function! Sd_set_shiftwidth() abort
    if &filetype == "BufFilePost" || &filetype == "TelescopePrompt" || &filetype == "help" || &filetype == "startuptime" || &filetype == "qf" || &filetype == "lspinfo"
      return
    endif

    set foldexpr=GetPotionFold(v:lnum)
    set foldtext=Sd_fold_text()
    set foldmethod=expr

    let fn = expand('%')

    if fn =~ '\.j2$'
      setlocal ft=jinja syntax=text commentstring=#\ %s
    elseif fn =~ '\.log$'
      setlocal ft=log
    elseif fn =~ '\.tfvars$' || fn =~ '\.terraform-vars$'
      setlocal ft=config
    elseif fn =~ 'Jenkinsfile.*'
      setlocal ft=groovy
    else
      if &filetype == "" || &filetype == "txt" || &filetype=="plugin" || &ft=='text'
        setlocal ft=text syntax=text commentstring=#\ %s conceallevel=2  nospell
        nnoremap <buffer> <silent> <cr> :call Sd_text_enter()<CR>
      elseif &ft == 'python'
        setlocal tabstop=4 softtabstop=4 shiftwidth=4
      elseif &ft == 'hcl'
        setlocal commentstring=//\ %s
      elseif &ft == 'javascript'
        setlocal commentstring=//\ %s
      elseif &ft == 'terraform'
        setlocal commentstring=#\ %s
      " elseif &ft == 'go'
      "   setlocal foldexpr=nvim_treesitter#foldexpr()
      elseif &ft=='cfg' || &ft=='config' || &ft=='conf' || &ft=='ini' || &ft=='systemd' || &ft=='gitconfig' || &ft=='dosini'
        setlocal commentstring=#\ %s
      elseif &ft == 'json'
        setlocal commentstring=//\ %s conceallevel=0
      elseif &ft == 'markdown'
        setlocal commentstring=//\ %s conceallevel=2
        setlocal nospell
      elseif &ft == 'autohotkey'
        setlocal commentstring=;\ %s
      elseif &ft == 'norg'
        nnoremap <buffer> <silent> <cr> :call Sd_neorg_enter()<CR>
        setlocal commentstring=#\ %s conceallevel=2 concealcursor=nv
      elseif &ft == 'c'
        setlocal commentstring=//\ %s
      else
        " setlocal commentstring=#\ %s
      endif
    endif
  endfunction

  if g:hostname =~ '^u20' || g:hostname =~ '^u18'
    set <S-TAB>=[9;2u
  endif

  if g:hostname == 'apple'
    hi! Folded ctermbg=NONE guibg=NONE guifg=#6c7d9c
  else
    hi! NeoTreeTitleBar guifg=#1e2030 guibg=#b8bb26
    hi! Folded ctermbg=NONE guibg=NONE guifg=#CABD9A  " grubox
    hi! SignColumn ctermbg=NONE guibg=NONE guifg=#CABD9A  " grubox
  endif

  hi! CursorLine ctermbg=16 cterm=none guibg=#121212

  augroup Sd_final_setting
    autocmd!
    autocmd FileType intersection_center,help,startuptime,qf,lspinfo nnoremap <buffer><silent> q :close<CR>
    autocmd FileType man nnoremap <buffer><silent> q :quit<CR>
    autocmd BufEnter,FileReadPost,BufReadPost * call Sd_set_shiftwidth()
  augroup end
